using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using TMPro;
using System;
using UnityEngine.UI;
using Sirenix.OdinInspector;

public class DataSystem : MonoBehaviour
{
    //UI
    public static Dictionary<string, BindableDataBase> dataPool;
    [RuntimeInitializeOnLoadMethod]
    public static void InitDataSystem()
    {
        if (dataPool != null)
            dataPool.Clear();
        dataPool = new Dictionary<string, BindableDataBase>();
        oneoffparam = new Dictionary<string, object>();
    }
    public static BindableDataBase GetBind(string dataName)
    {
        if (dataPool.ContainsKey(dataName))
            return dataPool[dataName];
        else
            return null;
    }
    public static void AddBind<T>(string name) => dataPool.Add(name, BindableData<T>.Create(name));
    public static void SetBind(BindableDataBase bindableData) => dataPool[bindableData.name] = bindableData;
    //Get端使用这个
    public static BindableDataBase BindData<T>(string dataName, Action<object> onModify = null)
    {
        BindableDataBase db;
        if (dataPool.ContainsKey(dataName))
        {
            db = dataPool[dataName];
            db.onModify += onModify;
        }
        else
        {
            db = BindableData<T>.Create(dataName);
            db.onModify += onModify;
            dataPool.Add(dataName, db);
        }
        db.Invoke<T>();
        return db;
    }
    public static void UnbindData(string dataName, Action<object> onModify)
    {
        BindableDataBase db;
        if (dataPool.ContainsKey(dataName))
        {
            db = dataPool[dataName];
            db.onModify -= onModify;

            if (db.onModify == null)
            {
                dataPool.Remove(dataName);
            }
        }
    }
    //上面的内容是有bind的系统用的, 那么单一的分散结构怎么用的, 还是只能DataSystem.set .get

    public static Dictionary<string, object> oneoffparam;

    public static void SetTrigger(string pname, object p) => oneoffparam[pname] = p;
    public static T GetTrigger<T>(string pname)
    {
        if (oneoffparam.TryGetValue(pname, out var obj) && obj != null)
        {
            oneoffparam[pname] = null;
            return (T)obj;
        }
        return default;
    }

}

[Serializable]
public class UIBindTarget
{
    public BindDataType bindType;

    [ShowIf("@bindType == BindDataType.TextMeshPro")] public TextMeshProUGUI textMeshProUGUI;
    [ShowIf("@bindType == BindDataType.Image")] public Image image;
    [ShowIf("@bindType == BindDataType.RawImage")] public RawImage rawImage;
    [ShowIf("@bindType == BindDataType.Slider")] public Slider slider;

    private int uid;
    public string dataName;
    public Action<object> onModify;
    [HideInInspector] public BindableDataBase linkedData;
    public void DoBind()
    {
        onModify = bindType switch
        {
            BindDataType.TextMeshPro => (val) => textMeshProUGUI.text = val as string,
            BindDataType.Image => (val) => image.sprite = val as Sprite,
            BindDataType.RawImage => (val) => rawImage.texture = val as Texture,
            BindDataType.Slider => (val) => slider.value = (float)val,
            _ => null
        };
        linkedData = bindType switch
        {
            BindDataType.TextMeshPro => DataSystem.BindData<string>(dataName, onModify),
            BindDataType.Image => DataSystem.BindData<Sprite>(dataName, onModify),
            BindDataType.RawImage => DataSystem.BindData<Texture>(dataName, onModify),
            BindDataType.Slider => DataSystem.BindData<float>(dataName, onModify),
            _ => null
        };
    }
    public void UnBind()
    {
        DataSystem.UnbindData(dataName, onModify);
    }
}
public enum BindDataType
{
    TextMeshPro,
    Image,
    RawImage,
    Slider
}

[Serializable]
public class BindableDataBase
{
    // public int uid;
    public string name;
    public Action<object> onModify;

    public void Invoke<T>()
    {
        var son = (BindableData<T>)this;
        son.Set(son.value);
    }
}
[Serializable]
public class BindableData<T> : BindableDataBase
{
    public T value;
    public BindableData<T> head;
    //Set端使用这个
    public BindableData(string name, T value)
    {
        this.name = name;
        this.value = value;
        var db = DataSystem.GetBind(name) as BindableData<T>;
        if (db == null)
        {
            head = this;
            DataSystem.SetBind(this);
        }
        else
        {
            head = db;
        }
        //传入一个初始化的参数
        Set(value);
    }

    private BindableData() { }
    public static BindableData<T> Create(string name)
    {
        var db = new BindableData<T>();
        db.name = name;
        db.value = default;
        db.head = db;
        return db;
    }

    public void Set(T value)
    {
        if (head == this)
        {
            onModify?.Invoke(value);
            this.value = value;
            return;
        }
        else
        {
            head.Set(value);
        }
    }

    public T Get()
    {
        if (head == this)
        {
            return value;
        }
        else
        {
            return head.Get();
        }
    }


}

public class ListableDataBase : IEnumerable
{
    public virtual IEnumerator GetEnumerator()
    {
        yield return null;
    }
}

